package com.waves.user.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.waves.common.event.UserBlackEvent;
import com.waves.common.event.UserRegisterEvent;
import com.waves.core.constant.TimeConstant;
import com.waves.core.constant.redis.UserRedisKey;
import com.waves.core.utils.AssertUtil;
import com.waves.core.utils.LoginUtils;
import com.waves.core.utils.Md5Util;
import com.waves.core.utils.RedisUtils;
import com.waves.core.web.domain.R;
import com.waves.core.web.domain.TableDataInfo;
import com.waves.user.build.UserBuilder;
import com.waves.user.cache.UserCache;
import com.waves.user.cache.UserSummaryCache;
import com.waves.user.dao.BlackDao;
import com.waves.user.dao.UserDao;
import com.waves.user.domain.dto.FollowPageDTO;
import com.waves.user.domain.dto.UserUpdateDTO;
import com.waves.user.domain.dto.user.BlackDTO;
import com.waves.user.domain.dto.user.ModifyNameDTO;
import com.waves.user.domain.dto.user.SummeryInfoDTO;
import com.waves.user.domain.entity.Black;
import com.waves.user.domain.entity.User;
import com.waves.core.domain.enums.BlackTypeEnum;
import com.waves.user.domain.vo.RelatedDataVO;
import com.waves.user.domain.vo.UserDetailVO;
import com.waves.user.domain.vo.UserInfoVO;
import com.waves.user.mapper.UserMapper;
import com.waves.user.service.UserService;
import javax.annotation.Resource;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserCache userCache;
    @Resource
    private UserDao userDao;
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    @Resource
    private BlackDao blackDao;
    @Resource
    private UserSummaryCache userSummaryCache;

    @Override
    public UserInfoVO getUserInfo(Long uid) {
        User userInfo = userCache.getUserInfo(uid);
        return UserBuilder.buildUserInfoResp(userInfo);
    }

    @Override
    @Transactional
    public void modifyName(ModifyNameDTO req) {
        Long uid = LoginUtils.getUserId();
        //判断名字是不是重复
        String newName = req.getName();
//        AssertUtil.isFalse(sensitiveWordBs.hasSensitiveWord(newName), "名字中包含敏感词，请重新输入"); // 判断名字中有没有敏感词
        User oldUser = userDao.getByName(newName);
        AssertUtil.isEmpty(oldUser, "名字已经被抢占了，请换一个哦~~");
        //改名
        userDao.modifyName(uid, req.getName());
        //删除缓存
        userCache.userInfoChange(uid);
    }

    @Override
    public void register(User user) {
        userDao.save(user);
        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, user));
    }

    @Override
    public void black(BlackDTO req) {
        Long uid = req.getUid();
        Black user = new Black();
        user.setTarget(uid.toString());
        user.setType(BlackTypeEnum.UID.getType());
        blackDao.save(user);
        User byId = userDao.getById(uid);
        blackIp(byId.getIpInfo().getCreateIp());
        blackIp(byId.getIpInfo().getUpdateIp());
        applicationEventPublisher.publishEvent(new UserBlackEvent(this, byId));
    }

    @Override
    public List<com.waves.user.domain.dto.SummeryInfoDTO> getSummeryUserInfo(SummeryInfoDTO req) {
        //需要前端同步的uid
        List<Long> uidList = getNeedSyncUidList(req.getReqList());
        //加载用户信息
        Map<Long, com.waves.user.domain.dto.SummeryInfoDTO> batch = userSummaryCache.getBatch(uidList);
        return req.getReqList()
                .stream()
                .map(a -> batch.containsKey(a.getUid()) ? batch.get(a.getUid()) : com.waves.user.domain.dto.SummeryInfoDTO.skip(a.getUid()))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public R<Object> followUser(long id) {
        // id被关注者 userId关注者
        // 一个人关注另一个人 自己会增加一个关注者 对方会增加一个被关注者
        Long userId = LoginUtils.getUserId();

        // 被关注者 增加一个粉丝或减少一个粉丝 followed
        String followedKey = UserRedisKey.getKey(UserRedisKey.USER_FOLLOWED_SET, id);
        User user = userDao.getById(id);
        Boolean member = RedisUtils.sIsMember(followedKey, userId);
        if (BooleanUtil.isTrue(member)) {
            user.setFollowed(user.getFollowed() - 1);
            RedisUtils.sRemove(followedKey, userId);
        } else {
            user.setFollowed(user.getFollowed() + 1);
            RedisUtils.sSetAndTime(followedKey, TimeConstant.YEAR_TIME, userId);
        }

        // 关注者 增加一个关注或减少一个关注 follow
        String followKey = UserRedisKey.getKey(UserRedisKey.USER_FOLLOW_SET, userId);
        User otherUser = userDao.getById(userId);
        Boolean otherMember = RedisUtils.sIsMember(followKey, id);
        if (BooleanUtil.isTrue(otherMember)) {
            otherUser.setFollow(otherUser.getFollow() - 1);
            RedisUtils.sRemove(followKey, userId);
        } else {
            otherUser.setFollow(otherUser.getFollow() + 1);
            RedisUtils.sSetAndTime(followKey, TimeConstant.YEAR_TIME, id);
        }
        userDao.updateBatchById(Arrays.asList(user, otherUser));

        // 如果互相关注 则自动成为好友
//        String followOtherKey = UserRedisKey.getKey(UserRedisKey.USER_FOLLOW_SET, id);
//        Boolean resultOne = RedisUtils.sIsMember(followOtherKey, userId);
//        Boolean resultTwo = RedisUtils.sIsMember(followKey, id);
//        if (resultOne && resultTwo) {
//            Room room = new Room().setType(2)
//                    .setHotFlag(0)
//                    .setActiveTime(new Date());
//            roomDao.save(room);
//
//            RoomFriend roomFriend = new RoomFriend().setRoomId(room.getId())
//                    .setUid1(id).setUid2(userId)
//                    .setRoomKey(id + "," + userId)
//                    .setStatus(0);
//            roomFriendDao.save(roomFriend);
//        }
        return R.ok();
    }

    /**
     * 查询关注列表
     *
     * @param dto
     * @return
     */
    @Override
    public TableDataInfo<UserDetailVO> listFollowUser(FollowPageDTO dto) {
        String key = null;
        // 0关注列表1粉丝列表
        if (0 == dto.getType()) {
            key = UserRedisKey.getKey(UserRedisKey.USER_FOLLOW_SET, dto.getUserId());
        } else {
            key = UserRedisKey.getKey(UserRedisKey.USER_FOLLOWED_SET, dto.getUserId());
        }
        Boolean isHave = RedisUtils.hasKey(key);
        if (isHave) {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            Set<String> userIds = RedisUtils.sGet(key);
            assert userIds != null;
            List<Long> collect = userIds.stream()
                    .map(Long::parseLong)
                    .collect(Collectors.toList());
            wrapper.in(User::getId, collect);
            IPage<UserDetailVO> page = baseMapper.selectPage(new Page<>(dto.getPageNum(), dto.getPageSize()), wrapper)
                    .convert(data -> {
                        if (data.getId().equals(dto.getUserId())) {
                            return null;
                        } else {
                            return BeanUtil.copyProperties(data, UserDetailVO.class);
                        }
                    });
            return TableDataInfo.build(page);
        }
        return TableDataInfo.build(new Page<>());
    }

    @Override
    public R<Object> updateUserInfo(UserUpdateDTO dto) {
        return R.ok(baseMapper.updateById(BeanUtil.copyProperties(dto, User.class)));
    }

    @Override
    public R<UserDetailVO> getUserInfos(long uid) {
        User user = baseMapper.selectById(uid);
        UserDetailVO vo = BeanUtil.copyProperties(user, UserDetailVO.class);
        List<RelatedDataVO> list = new ArrayList<>();
        // todo 远程查询task服务
//        list.add(new RelatedDataVO().setLabel("帖子")
//                .setNum(RedisUtils.sGetSetSize(UserRedisKey.getKey(UserRedisKey.USER_FOLLOWED_SET, uid))));
//        list.add(new RelatedDataVO().setLabel("任务")
//                .setNum(RedisUtils.sGetSetSize(UserRedisKey.getKey(UserRedisKey.USER_FOLLOWED_SET, uid))));
        list.add(new RelatedDataVO().setLabel("关注")
                .setNum(RedisUtils.sGetSetSize(UserRedisKey.getKey(UserRedisKey.USER_FOLLOW_SET, uid))));
        list.add(new RelatedDataVO().setLabel("粉丝")
                .setNum(RedisUtils.sGetSetSize(UserRedisKey.getKey(UserRedisKey.USER_FOLLOWED_SET, uid))));
        vo.setList(list);
        return R.ok(vo);
    }

    /**
     * 根据key查询用户集合
     *
     * @param key
     * @return
     */
    private List<User> getListByKey(String key) {
        Boolean isHave = RedisUtils.hasKey(key);
        if (isHave) {
            Set<String> userIds = RedisUtils.sGet(key);
            assert userIds != null;
            return baseMapper.selectBatchIds(new ArrayList<>(userIds));
        }
        return null;
    }

    private List<Long> getNeedSyncUidList(List<SummeryInfoDTO.infoReq> reqList) {
        List<Long> needSyncUidList = new ArrayList<>();
        List<Long> userModifyTime = userCache.getUserModifyTime(reqList.stream().map(SummeryInfoDTO.infoReq::getUid).collect(Collectors.toList()));
        for (int i = 0; i < reqList.size(); i++) {
            SummeryInfoDTO.infoReq infoReq = reqList.get(i);
            Long modifyTime = userModifyTime.get(i);
            if (Objects.isNull(infoReq.getLastModifyTime()) || (Objects.nonNull(modifyTime) && modifyTime > infoReq.getLastModifyTime())) {
                needSyncUidList.add(infoReq.getUid());
            }
        }
        return needSyncUidList;
    }

    public void blackIp(String ip) {
        if (StrUtil.isBlank(ip)) {
            return;
        }
        try {
            Black user = new Black();
            user.setTarget(ip);
            user.setType(BlackTypeEnum.IP.getType());
            blackDao.save(user);
        } catch (Exception e) {
//            log.error("duplicate black ip:{}", ip);
        }
    }

    @Override
    public SaResult login(String email, String password) throws Exception {
        //根据用户名从数据库中查询
        User user = this.getOne(Wrappers.lambdaQuery(User.class).eq(User::getName, email));
        System.out.println(user);
        if (user == null) {
            return SaResult.error("用户名不存在");
        }
        //对登录密码进行加密
        boolean result = Md5Util.passwordVerify(password, user.getName(), Md5Util.md5Key);
        if (result) {
            //根据用户id登录，第1步，先登录上
            StpUtil.login(user.getId());
            // 第2步，获取 Token  相关参数
            SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
            // 第3步，返回给前端
            return SaResult.data(tokenInfo);
        }
        return SaResult.error("密码错误");
    }

    @Override
    public SaResult logout() {
        return null;
    }

}
